import requestConfig from '@/config';
import { head, findIndex } from 'lodash';
import { isIos, getSystemInfo, isMultipleAuthor, secondFormatMinute } from '@/utils';

import { getALYUploadToken, getHWUploadToken } from '@/api/material';

const getPolicyEncode = require('@/SDK/HWOBS/getPolicy.js');
const getSignature = require('@/SDK/HWOBS/GetSignature.js');

const {
	FILE_TRANSFER: { HUAWEIYUN }
} = requestConfig;

class EncapsulationUpload {
	isNetwork = false;
	currentRequest = 0;
	isAllAbort = false;

	stintFormatFile = ['mp4'];
	maxSize = 100 * 1024 * 1024;
	proportion = 20 * 1024 * 1024;
	requestTimeout = 1_000 * 60 * 10;

	HWOBSConfig = {
		EndPoint: HUAWEIYUN.EndPoint,
		conditions: [
			{ bucket: HUAWEIYUN.bucket },
			{
				'x-obs-acl': 'public-read'
			}
		]
	};

	ALYOSSConfig = {};

	debounceProgressUpdate = (params, { progress }) => {
		const { direction } = this;

		direction.$set(params, 'status', 'upload');
		direction.$set(params, 'percent', progress);
	};

	constructor(params) {
		const {
			that,

			maxUpload = 5,
			maxRequest = 6,
			autoupload = true,
			isReplace = false,
			requestParams = {},
			sourceType = ['album'],
			statusNumber = {
				maxImage: 10,

				maxVideo: 6
			},
			videoLimit = {
				minSize: 0,
				maxSize: 0,

				minDuration: 5, // 秒
				maxDuration: 30 // 秒
			},
			repeatHintObject = {
				image: '请勿重复提交相同图片',
				video: '请勿重复提交相同视频文件',
				videoLimit: '请上传5s-30s内的视频文件'
			}
		} = params;

		const { version } = getSystemInfo();

		this.direction = that;
		this.isReplace = isReplace;
		this.maxUpload = maxUpload;
		this.maxRequest = maxRequest;
		this.autoupload = autoupload;

		this.wxVersion = version;
		this.sourceType = sourceType;
		this.statusNumber = statusNumber;

		this.videoLimit = videoLimit; // 权重比 size > duration
		this.requestParams = requestParams;
		this.repeatHintObject = repeatHintObject;
	}

	openPhotoAlbum(type) {
		const {
			wxVersion,
			isReplace,
			sourceType,
			stintFormatFile,
			direction: { uploadStatusFileList },
			statusNumber: { maxImage = 0, maxVideo = 0 }
		} = this;

		const status = isMultipleAuthor();

		if (!status) {
			return;
		}

		let mediaType = [];

		const isImage = type === 'image' || !type;
		const imageNumber = isImage ? (uploadStatusFileList.find((item) => item.type === 'image')?.list || []).length : maxImage;
		const imageSurplus = maxImage - imageNumber;
		if (maxImage && imageSurplus) {
			mediaType.push('image');
		}

		const isVideo = type === 'video' || !type;
		const videoNumber = isVideo ? (uploadStatusFileList.find((item) => item.type === 'video')?.list || []).length : maxVideo;
		const videoSurplus = maxVideo - videoNumber;
		if (maxVideo && videoSurplus) {
			mediaType.push('video');
		}

		/** 剩余文件上传数量 */
		let surplusFilesNum = maxVideo + maxImage - (imageNumber + videoNumber);

		if (!surplusFilesNum && !isReplace) {
			return;
		}

		isReplace && (surplusFilesNum = maxImage);

		/** 取消微信版本限制选取图片/视频数量 */
		if (isIos()) {
			const num = wxVersion.substring(0, 1) * 1 >= 8 ? 20 : 9;
			surplusFilesNum = surplusFilesNum > num ? num : surplusFilesNum;
		}

		videoSurplus && this.getHWTemporaryUploadCertificate();
		imageSurplus && this.getALYemporaryUploadCertificate();

		uni.chooseMedia({
			mediaType,
			sourceType,
			count: surplusFilesNum,
			sizeType: ['original'],

			success: (res) => {
				let { type, tempFiles = [] } = res;
				/**
				 * 判断图片 || 视频是否超过限制
				 */
				const len = tempFiles.length;
				const temporaryType = head(tempFiles)?.fileType || type;

				if (surplusFilesNum < len) {
					uni.showToast({
						mask: true,
						icon: 'none',
						duration: 2000,
						title: `当前最多可上传${surplusFilesNum}个文件`
					});
					return;
				}

				if (temporaryType === 'video') {
					const formatList = tempFiles.filter((item) => {
						const { tempFilePath } = item;
						const type = tempFilePath.substring(tempFilePath.lastIndexOf('.') + 1);

						return stintFormatFile.includes(type);
					});

					if (formatList.length < tempFiles.length) {
						uni.showToast({
							mask: true,
							icon: 'none',
							title: '视频仅支持MP4格式'
						});
						tempFiles = formatList;
					}
				}

				isReplace && (this.direction.uploadStatusFileList = []);

				tempFiles.length &&
					this.checkStatusRepeat({
						tempFiles,
						temporaryType
					});
			}
		});
	}

	async checkStatusRepeat({ tempFiles, temporaryType }) {
		const {
			getUuid,
			direction,
			isNetwork,
			autoupload,
			repeatHintObject,
			direction: { uploadStatusFileList },
			videoLimit: { minSize, maxSize, minDuration, maxDuration }
		} = this;

		const index = findIndex(uploadStatusFileList, (item) => item.type === temporaryType);

		const temporaryList = uploadStatusFileList[index]?.list || [];

		/**
		 * 过滤重复图片 || 视频
		 */
		let list = tempFiles
			.map((item) => {
				/**
				 * duration 时长
				 * thumbTempFilePath 首帧地址
				 */
				const { size, width, height, fileType, tempFilePath, duration, thumbTempFilePath } = item;

				if (temporaryList.length && temporaryList.some((item) => item.size === size)) {
					return;
				}

				return Object.assign(
					{
						size,
						tempFilePath,
						status: 'wait',
						type: fileType || temporaryType,
						name: `LL_${temporaryType.toUpperCase()}_${getUuid()}${tempFilePath.substring(tempFilePath.lastIndexOf('.'))}`
					},
					temporaryType === 'image'
						? {
								previewUrl: tempFilePath
						  }
						: {
								width,
								height,
								duration,
								previewUrl: thumbTempFilePath
						  }
				);
			})
			.filter(Boolean);
		const listLength = list.length;

		if (listLength < tempFiles.length) {
			uni.showToast({
				mask: true,
				icon: 'none',
				title: repeatHintObject[temporaryType]
			});
		}

		const isImage = temporaryType === 'image';

		if (!isImage) {
			/**
			 * 过来播放时长（10秒到30秒）
			 */

			list = list
				.map((item) => {
					const { size, duration } = item;

					if ((minSize && size < minSize) || (maxSize && size > maxSize)) {
						return;
					}

					if ((minDuration && duration < minDuration) || (maxDuration && duration > maxDuration)) {
						return;
					}

					return {
						...item,
						duration: secondFormatMinute(duration)
					};
				})
				.filter(Boolean);

			if (list.length < listLength) {
				uni.showToast({
					mask: true,
					icon: 'none',
					title: repeatHintObject['videoLimit']
				});
			}
		}

		if (!list.length) {
			return;
		}

		if (index === -1) {
			uploadStatusFileList.push({
				list,
				type: temporaryType
			});
		} else {
			uploadStatusFileList[index].list.push(...list);
		}

		if (!isNetwork && !(await this.getCurrentNetWorkStatus())) {
			uploadStatusFileList.forEach((item) => {
				const { list } = item;

				list.forEach((subItem) => {
					const { status, fileTask } = subItem;

					fileTask && fileTask?.abort();

					status !== 'success' && direction.$set(subItem, 'status', 'error');
				});
			});
			return;
		}

		if (!autoupload) {
			return;
		}

		isImage ? this.ALYOSSUploadImage(temporaryType) : this.HWOBSUploadVide(temporaryType);
	}

	HWOBSUploadVide(type = 'video') {
		const {
			direction,
			maxRequest,
			currentRequest,
			requestTimeout,
			HWOBSConfig: { expiration, EndPoint, conditions, AccessKeyID, SecretKey, HWToken }
		} = this;

		if (currentRequest >= maxRequest) {
			return;
		}

		const list = direction.uploadStatusFileList.find((item) => item.type === type)?.list || [];

		if (!list.length || list.every((item) => item.fileTask)) {
			return;
		}

		list.forEach((subItem) => {
			const { name, size, status, type, fileTask, tempFilePath } = subItem;

			if (fileTask || this.currentRequest >= maxRequest || status === 'success') {
				return;
			}

			++this.currentRequest;

			const OBSPolicy = {
				expiration,
				conditions: [
					...conditions,
					{
						key: name
					},
					{ 'content-type': type },
					{ 'x-obs-security-token': HWToken }
				]
			};

			const policy = getPolicyEncode(OBSPolicy);
			const signature = getSignature(policy, SecretKey);

			const formData = {
				policy,
				signature,
				key: name,
				AccessKeyID,

				'content-type': type,
				'x-obs-acl': 'public-read',
				'x-obs-security-token': HWToken
			};

			const uploadTask = uni.uploadFile({
				formData,
				url: EndPoint,
				filePath: tempFilePath,
				timeout: requestTimeout,

				name: 'file',
				header: {
					'content-type': 'multipart/form-data; boundary=7e32233530b26'
				},

				success: (res) => {
					const { statusCode, header } = res || {};
					const { Date, ...otherParams } = header;

					// const wait = this.waitSynchronizeTime(size);
					const isSuccess = statusCode === 204;

					// setTimeout((e) => {
					direction.$set(subItem, 'status', isSuccess ? 'success' : 'error');

					direction.$set(subItem, 'otherParams', {
						statusCode,
						...otherParams,
						resourceType: 2 // 素材类型：1 图片 2 视频
					});
					// }, wait * 1000);
				},

				fail: (res) => {
					direction.$set(subItem, 'status', 'error');
				},

				complete: (res) => {
					--this.currentRequest;
					this.uploadSuccessCallback();
				}
			});

			direction.$set(subItem, 'fileTask', uploadTask);

			uploadTask.onProgressUpdate(({ progress }) => {
				const formatProgress = progress >= 95 ? 95 : progress;

				this.debounceProgressUpdate(subItem, {
					progress: formatProgress
				});
			});
		});
	}

	ALYOSSUploadImage(type = 'image') {
		const {
			direction,
			maxRequest,
			currentRequest,
			ALYOSSConfig: { EndPoint, originalPath, ...otherParams }
		} = this;

		if (currentRequest >= maxRequest) {
			return;
		}

		const list = direction.uploadStatusFileList.find((item) => item.type === type)?.list || [];

		if (!list.length || list.every((item) => item.fileTask)) {
			return;
		}

		list.forEach((item) => {
			const { name, status, fileTask, tempFilePath } = item;

			if (fileTask || this.currentRequest >= maxRequest || status === 'success') {
				return;
			}

			++this.currentRequest;

			const formData = {
				key: `${originalPath}${name}`,
				...otherParams
			};

			const uploadTask = uni.uploadFile({
				formData,
				url: EndPoint,
				filePath: tempFilePath,

				name: 'file',
				header: {
					'content-type': 'multipart/form-data'
				},

				success: (res) => {
					const { statusCode, header } = res || {};
					const { Date, ...otherParams } = header;

					direction.$set(item, 'status', 'success');

					direction.$set(item, 'otherParams', {
						statusCode,
						...otherParams,
						resourceType: 1 // 素材类型：1 图片 2 视频
					});
				},

				fail: (res) => {
					direction.$set(item, 'status', 'error');
				},

				complete: () => {
					--this.currentRequest;
					this.uploadSuccessCallback();
				}
			});

			direction.$set(item, 'fileTask', uploadTask);

			uploadTask.onProgressUpdate((params) => this.debounceProgressUpdate(item, params));
		});
	}

	uploadSuccessCallback() {
		const { isAllAbort } = this;

		if (isAllAbort) {
			return;
		}

		this.HWOBSUploadVide();
		this.ALYOSSUploadImage();
	}

	getUuid() {
		return 'xxxxxxxxxxxx4xxxyxxxxxxxxxxxxxxx'.replace(/[xy]/g, (c) => {
			const r = (Math.random() * 16) | 0;
			const v = c == 'x' ? r : (r & 0x3) | 0x8;

			return v.toString(16);
		});
	}

	getHWTemporaryUploadCertificate() {
		const {
			requestParams,
			HWOBSConfig: { expiration }
		} = this;

		if (expiration && Date.now() - 1_0000 < new Date(expiration).getTime()) {
			return;
		}

		getHWUploadToken(requestParams).then((res) => {
			const { expires_at, securitytoken, access, secret } = res?.data?.credential || {};

			this.HWOBSConfig = {
				...this.HWOBSConfig,

				SecretKey: secret,
				AccessKeyID: access,
				expiration: expires_at,
				HWToken: securitytoken
			};
		});
	}

	getALYemporaryUploadCertificate() {
		const {
			requestParams,
			ALYOSSConfig: { expiration }
		} = this;

		if (expiration && Date.now() - 1_0000 < expiration) {
			return;
		}

		getALYUploadToken(requestParams).then((res) => {
			const { accessId, expires_at, host, policy, signature, originalPath } = res?.data || {};

			this.ALYOSSConfig = {
				...this.ALYOSSConfig,

				policy,
				signature,
				originalPath,
				EndPoint: host,
				expiration: expires_at,
				OSSAccessKeyId: accessId
			};
		});
	}

	deleteSingleFile(params) {
		const { direction } = this;
		const { type, index } = params;

		const targetIndex = direction.uploadStatusFileList.findIndex((item) => item.type === type);
		const list = direction.uploadStatusFileList[targetIndex]?.list || [];

		list.splice(index, 1);
		!list.length && direction.uploadStatusFileList.splice(targetIndex, 1);
	}

	singleAgainUpload(params) {
		const { direction } = this;

		const { type, index } = params;
		const list = direction.uploadStatusFileList.find((item) => item.type === type)?.list || [];

		direction.$set(list[index], 'status', 'wait');
		direction.$set(list[index], 'fileTask', undefined);

		this.uploadSuccessCallback();
	}

	singleAbortUpload(params) {
		const { direction } = this;
		const { type, index } = params;

		const targetIndex = direction.uploadStatusFileList.findIndex((item) => item.type === type);
		const list = direction.uploadStatusFileList[targetIndex]?.list || [];

		list[index].fileTask?.abort();

		direction.$set(list[index], 'status', 'error');
		direction.$set(list[index], 'fileTask', undefined);
	}

	allAbortUpload() {
		this.isAllAbort = true;

		const { direction } = this;

		direction.uploadStatusFileList.forEach((item) => {
			const { list } = item;

			list.forEach((subItem) => {
				const { status, fileTask } = subItem;

				if (status === 'upload') {
					fileTask?.abort();
					direction.$set(subItem, 'status', 'error');
					direction.$set(subItem, 'fileTask', undefined);
				}
			});
		});
	}

	waitSynchronizeTime(size) {
		const { maxSize, proportion } = this;
		/**
		 *  < 10m 2s
		 * 	< 50m 3s
		 * 	< 100m 4s
		 * 	> 100m 5s
		 */

		if (size > maxSize) {
			return 5;
		}

		const diff = (size / proportion).toFixed(2);

		return diff < 2 ? 2 : diff;
	}

	getCurrentNetWorkStatus() {
		return new Promise((resolve) => {
			uni.getNetworkType({
				success: (res) => {
					const { networkType } = res;

					if (networkType === 'wifi') {
						resolve(true);
						return;
					}

					uni.showModal({
						title: '提示',
						content: '当前网络处于非Wi-Fi状态，是否继续上传？',
						success: (res) => {
							const { confirm } = res;

							confirm && (this.isNetwork = true);
							resolve(confirm);
						}
					});
				}
			});
		});
	}
}

export default EncapsulationUpload;
